package project;
import java.sql.*;
import java.io.*;
import java.security.*;
import java.lang.Math;
import java.util.ArrayList;

public class Checksum{
    DatabaseInfo db;
    private int checkSum = 0;
    private long checkHash = 0;
    public int getCheckSum(){
        return checkSum;
    }
    public long getCheckHash(){
        return checkHash;
    }

    public Checksum() {

    try {

           Class.forName("com.mysql.jdbc.Driver").newInstance();
	} catch (Exception e) {
            System.err.println("Error loading Driver");
	}
    try {
        FileInputStream fis = new FileInputStream("local.dat");
        ObjectInputStream ois = new ObjectInputStream(fis);
        db = (DatabaseInfo)ois.readObject();
        int x = getRowNumbers(db);
        try{
        checkSum = calculateChecksumB(db, 1, 1);
        } catch (SingleRecordException e){

        }
       // System.out.println(checkSum+"local 1x");

    } catch (IOException e){
        System.err.println(e.getMessage());//
    } catch (ClassNotFoundException e){
        System.err.println(e.getMessage());//
    }
}

//public Checksum(DatabaseInfo database){
//    try {
//           Class.forName("com.mysql.jdbc.Driver").newInstance();
//	} catch (Exception e) {
//            System.out.println("Error loading Driver");
//	}
//    int x = getRowNumbers(database);
//    checkSum = calculateChecksum1(database, 1, x);
//    //System.out.println(checkSum+ "other 1x");
//}

public Checksum(DatabaseInfo database, int fraction, int position) throws SingleRecordException{
    try {
           Class.forName("com.mysql.jdbc.Driver").newInstance();
	} catch (Exception e) {
            System.err.println("Error loading Driver");
	}
    checkSum = calculateChecksumB(database, fraction, position);

   // System.out.println(checkSum+ "other ab");
}

public Checksum(int fraction, int position) throws SingleRecordException{
    try {
           Class.forName("com.mysql.jdbc.Driver").newInstance();
	} catch (Exception e) {
            System.err.println("Error loading Driver");
	}
    try {
        FileInputStream fis = new FileInputStream("local.dat");
        ObjectInputStream ois = new ObjectInputStream(fis);
        db = (DatabaseInfo)ois.readObject();
        checkSum = calculateChecksumB(db, fraction, position);
        //System.out.println(checkSum + "local ab");

    } catch (IOException e){
        System.err.println(e.getMessage());//
    } catch (ClassNotFoundException e){
        System.err.println(e.getMessage());//
    }
}


public int getRowNumbers(DatabaseInfo db){
    int n = 0;
    Connection conn = null;
    try{
    conn = DriverManager.getConnection("jdbc:mysql://" + db.getHostName()+":"+db.getPortNo()+"/"+db.getDatabaseName(),
		db.getUserName(),db.getPassword());
        Statement stmt = conn.createStatement();
        ResultSet rs = conn.getMetaData().getTables("", "", "", null);
        while (rs.next()){
            ResultSet rows = stmt.executeQuery("Select count(*) from " + rs.getString(3));
            while (rows.next()){
                n+= Integer.parseInt(rows.getString(1));
            }
        }
        conn.close();
    } catch (SQLException e){
        System.err.println(e.getMessage());//
    }    
    return n;
}
public  int calculateChecksumP(DatabaseInfo db, int fraction, int position) throws SingleRecordException{
    int i = 0;
    int numRows = getRowNumbers(db);
    float x = (numRows* (position -1))/fraction +1 ;
    int start = (int)x ;
    int finish = (numRows * position) / fraction ;
if (finish - start >= 0){
    int recordCount = start;
    Connection conn = null;
    ResultSet values = null;
    ResultSet primary = null;
    ResultSet tables = null;
    try{
        conn = DriverManager.getConnection("jdbc:mysql://" + db.getHostName()+":"+db.getPortNo()+"/"+db.getDatabaseName(),
		db.getUserName(),db.getPassword());
        Statement stmt = conn.createStatement();
        tables = conn.getMetaData().getTables("", "", "", null);
        int endPoint = finish;
    while (tables.next()) {
        ResultSet rows = stmt.executeQuery("select count(*) from "+ tables.getString(3));
        rows.absolute(1);
        int rowNum = Integer.parseInt(rows.getString(1));
        rows.close();
        if (rowNum < recordCount) {
            recordCount -= rowNum;
            endPoint -= rowNum;
        } else {
            primary = conn.getMetaData().getPrimaryKeys("", "", tables.getString(3));
            while (primary.next()) {
            values = stmt.executeQuery("select " + primary.getString(4) + " from " + tables.getString(3));

            if (recordCount >1){
            values.absolute(recordCount-1);

            }
            while (values.next() && recordCount <= endPoint){

                    i = primaryKeyTotal(i,values);
                    //i += Integer.parseInt(values.getString(1));
                recordCount++;
            }
        }
       recordCount -= rowNum;
        endPoint -= rowNum;
        }

    }

    conn.close();
    } catch (SQLException e) {
		System.err.println(e.getMessage());
    }
if (start == finish){
    throw new SingleRecordException();

}
}

    return i;
}

public int primaryKeyTotal(int current, ResultSet values) throws SQLException{
    int j = current;
    j += Integer.parseInt(values.getString(1));
    return j;
}

public  int calculateChecksumB(DatabaseInfo db, int fraction, int position) throws SingleRecordException{
    int i = 0;
    int numRows = getRowNumbers(db);
    float x = (numRows* (position -1))/fraction +1 ;
    int start = (int)x ;
    int finish = (numRows * position) / fraction ;
    System.out.println("    Start: "+ start + ", Finish: "+finish + "db: "+ db.getDatabaseName());
if (finish - start >= 0){
    int recordCount = start;
    Connection conn = null;
    ResultSet values = null;
    ResultSet tables = null;
    try{
        conn = DriverManager.getConnection("jdbc:mysql://" + db.getHostName()+":"+db.getPortNo()+"/"+db.getDatabaseName(),
		db.getUserName(),db.getPassword());
        Statement stmt = conn.createStatement();
        tables = conn.getMetaData().getTables("", "", "", null);
        int endPoint = finish;
    while (tables.next()) {
        ResultSet rows = stmt.executeQuery("select count(*) from "+ tables.getString(3));

        rows.absolute(1);
        int rowNum = Integer.parseInt(rows.getString(1));
        rows.close();
        if (rowNum < recordCount) {
            recordCount -= rowNum;
            endPoint -= rowNum;
        } else {
            values = stmt.executeQuery("select * from " + tables.getString(3));

            if (recordCount >1){
            values.absolute(recordCount-1);

            }
            while (values.next() && recordCount <= endPoint){

                int j = 1;
        while (true){
            try{
            //System.out.print(values.getString(j) + " ");
            byte[] byteArray = getBytes(values.getString(j));
            //i += adler32(byteArray) % 65521;
            i += fletcher(byteArray) % 65535;
            //i += sha1(byteArray);
            j++;

            } catch (Exception e){
                break;
            }
        }
                    //i += Integer.parseInt(values.getString(1));
                recordCount++;
            }

       recordCount -= rowNum;
        endPoint -= rowNum;
        }

    }

    conn.close();
    } catch (SQLException e) {
		System.err.println(e.getMessage());//
    }
if (start == finish){
    throw new SingleRecordException();
}
}
    return i;
}

  public byte[] getBytes(Object obj) throws IOException{
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(bos);
      oos.writeObject(obj);
      oos.flush();
      oos.close();
      bos.close();
      byte [] data = bos.toByteArray();
      return data;
  }

  public int adler32(byte[] bytes){
      //System.out.println("adler32");
      int a = 1;
      int b = 0;
      for (int j=0;j<bytes.length;j++){
          a += bytes[j] % 65521;
          b += a % 65521;
      }
      return (b << 16 | a);
  }

  public int fletcher(byte[] bytes){
      //System.out.println("Fletcher");
      int a = 1;
      int b = 0;
      for (int j=0;j<bytes.length;j+=2){
          try{
          a += (bytes[j] << 8 | bytes[j+1]) % 65535;
          } catch (IndexOutOfBoundsException e){
              a += (bytes[j]<<8) % 65535;
          }
          b += a % 65535;
      }
      return (b << 16 | a);
  }
  public  long calculateChecksumW(DatabaseInfo db, int fraction, int position) throws SingleRecordException{
    long i = 0;
    int numRows = getRowNumbers(db);
    float x = (numRows* (position -1))/fraction +1 ;
    int start = (int)x ;
    int finish = (numRows * position) / fraction ;
    ArrayList<Integer> wordList = new ArrayList<Integer>();
if (finish - start >= 0){
    int recordCount = start;
    Connection conn = null;
    ResultSet values = null;
    ResultSet tables = null;
    try{
        conn = DriverManager.getConnection("jdbc:mysql://" + db.getHostName()+":"+db.getPortNo()+"/"+db.getDatabaseName(),
		db.getUserName(),db.getPassword());
        Statement stmt = conn.createStatement();
        tables = conn.getMetaData().getTables("", "", "", null);
        int endPoint = finish;
    while (tables.next()) {
        ResultSet rows = stmt.executeQuery("select count(*) from "+ tables.getString(3));
        rows.absolute(1);
        int rowNum = Integer.parseInt(rows.getString(1));
        rows.close();
        if (rowNum < recordCount) {
            recordCount -= rowNum;
            endPoint -= rowNum;
        } else {
            values = stmt.executeQuery("select * from " + tables.getString(3));

            if (recordCount >1){
            values.absolute(recordCount-1);

            }
            while (values.next() && recordCount <= endPoint){
        int j = 1;
        while (true){
            try{
            //System.out.print(values.getString(j) + " ");
            byte[] byteArray = getBytes(values.getString(j));
            for (int k=0;k<byteArray.length;k+=4){
                try{
                wordList.add((byteArray[k]<<24)|(byteArray[k+1]<<16)|(byteArray[k+2]<<8)|(byteArray[k+3]));
                } catch (ArrayIndexOutOfBoundsException ea){
                    int r=0;
                    int l;
                    for (l=0;l<byteArray.length -1%4;l++){
                        r = (r<<8 | byteArray[k+l]);
                    }
                    for (;l<3;l++){
                        r = (r<<8 | (byte)0);
                    }
                    wordList.add(r);
                }
                            }
            //i += adler32(byteArray) % 65521;
            j++;
            } catch (Exception e){
               // System.out.println(e.getMessage()+ "!");
                //System.out.println();
                break;
            }
        }
                    //i += Integer.parseInt(values.getString(1));
                recordCount++;
            }

       recordCount -= rowNum;
        endPoint -= rowNum;
        }

    }
    i = sha1(wordList);

    conn.close();
    } catch (SQLException e) {
		System.err.println(e.getMessage());
    }
if (start == finish){
    throw new SingleRecordException();
}
}
    return i;
}
public long sha1(ArrayList<Integer> words){
      long i = 0;
      int len = words.size()*32;
      words.add(0x80000000);
for (int j=0;words.size()%16!=15;j++){
    words.add(0);
}
      words.add(0);
words.add(len);
//System.out.println("Chunk: "+ words.get(words.size() -7));
int h0 = 0x67452301;int h1 = 0xEFCDAB89;int h2 = 0x98BADCFE;int h3 = 0x10325476;int h4 = 0xC3D2E1F0;

for (int j=0;j<words.size();j+=16){
    int[] w = new int[80];
    for (int k=0;k<16;k++){
        try{
        w[k] = words.get(j+k);
        } catch (IndexOutOfBoundsException e){
            //System.out.println("Bah "+ (j+k));
            w[k] = 0;
        }
    }
    for (int k=16;k<80;k++){
        int x = (w[k-3] ^ w[k-8] ^ w[k-14] ^ w[k-16]);
        w[k] = (((x)<<1)|((x)>>>(31)));
    }
    int a = h0;int b = h1;int c = h2;int d = h3; int e = h4;
    for (int k=0;k<80;k++){
        int t1 = 0; int t2 = 0;
        if (k<20){
            t1 = (b & c) | ((~b) & d);
            t2 = 0x5A827999;
        } else if (k<40){
            t1 = b ^ c ^ d;
            t2 = 0x6ED9EBA1;
        } else if (k<60){
            t1 = (b & c) | (b & d) | (c & d);
            t2 = 0x8F1BBCDC;
        } else if (k<80){
            t1 = b ^ c ^ d;
            t2 = 0xCA62C1D6;
        }
        int temp = (((a)<<5)|((a)>>>(27))) + t1 + e + t2 + w[k];
        e = d; d = c; c = (((b)<<30)|((b)>>>(2)));b = a;a = temp;
    }
    h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;
}
//List<Byte> byteList = new ArrayList<Byte>(Arrays.asList(bytes));


i = (h0 << 128 | h1 << 96 | h2 << 64 | h3 << 32 | h4);
      return i;
  }
public String md5(ArrayList<Integer> words){
      String i = null;

byte[] bytes = new byte[words.size()* 4];
for (int j=0;j<words.size();j++){
    byte[] bytes1 = new byte[4];
    try{
    bytes1 = getBytes(words.get(j));
    } catch (IOException e){
        System.err.println(e.getMessage()); //
    }
    for (int k=0;k<4;k++){
        //.out.println(j +", k"+ k);
        bytes[j*4+k] = bytes1[k];
    }
}

try{
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(bytes);
i = (md.digest().toString());
} catch (Exception e){
    System.err.println(e.getMessage());//
}

return i;
}

}
